#include "export.h"

#include "tinyxml.h"

Export::Export(string typeDocFileName)
{
	mParentModuleData = NULL;
	//gets the SystemC2XML/Settings path
	string filePath = string(getenv(ROOT_DIR)).append("Settings\\");
	//Gets a filepath and loads it
	TiXmlDocument typeDoc( filePath.append(typeDocFileName).c_str() );
	typeDoc.LoadFile();

	TiXmlElement* element = typeDoc.FirstChildElement("Types")->FirstChildElement("Type");

	string type, name;
	//For all siblings the attributes name and Type are mapped into name and type 
	while (element)
	{
		name = element->Attribute("name");
		type = element->Attribute("type");
		mTypeMap[name] = type;
		element = element->NextSiblingElement("Type");
	}
}
/*This is the write function which calls these functions to generate an output
*/
void Export::write(string outputName, sc_object* top)
{
	clear();
	mParentModuleData = createModuleDataTree(top);
	createChannelData();
	generateOutput(outputName);
}
/* this function clears port interface and Interface list by iterating through and deleting. 
It then deletes the value at ParentModuleData and making it equal to NULL
*/
void Export::clear()
{
	list<ChannelDataInfo*>::iterator i;

	for (i = mPortInterface.begin(); i != mPortInterface.end(); i++) delete (*i);
	for (i = mInterfaceList.begin(); i != mInterfaceList.end(); i++) delete (*i);


	mPortInterface.clear();
	mInterfaceList.clear();

	delete mParentModuleData;
	mParentModuleData = NULL;
}
//Destructor
Export::~Export()
{
	clear();
}

//This function creates the channel data.
void Export::createChannelData()
{
	ChannelData* channelData;
	for (list<ChannelDataInfo*>::iterator i = mPortInterface.begin(); i != mPortInterface.end(); i++)
	{
		for (list<ChannelDataInfo*>::iterator j = mInterfaceList.begin(); j != mInterfaceList.end(); j++)
		{
			if ((*i)->getTargetAddress() == (*j)->getTargetAddress())
			{
				InOutType first = (*i)->getInOutType();
				InOutType second = (*j)->getInOutType();

				//Check the direction of the channel
				if ( (first == TYPE_IN && second != TYPE_IN) || (second == TYPE_OUT && first == TYPE_INOUT) )
				{	//If channel goes from j to i
					channelData = new ChannelData();
					channelData->setTarget((*i)->getModuleData(), (*i)->getName(), (*i)->getType());
					channelData->setSource((*j)->getModuleData(), (*j)->getName(), (*j)->getType());
					
				}
				else if( (second == TYPE_IN && first != TYPE_IN) || (first == TYPE_OUT && second == TYPE_INOUT) )
				{ //If channel goes from i to j
					channelData = new ChannelData();
					channelData->setSource((*i)->getModuleData(), (*i)->getName(), (*i)->getType());
					channelData->setTarget((*j)->getModuleData(), (*j)->getName(), (*j)->getType());
				}
				else
				{ //Channel doesn't have a direction
					channelData = new ChannelData(true);
					channelData->setSource((*i)->getModuleData(), (*i)->getName(), (*i)->getType());
					channelData->setTarget((*j)->getModuleData(), (*j)->getName(), (*j)->getType());
				}
				(*i)->getModuleData()->addChannelData(channelData);
				break;
			}
		}
	}
}
//This function creates a module data tree. 
ModuleData* Export::createModuleDataTree(sc_object* top)
{
	vector<sc_object*> children = top->get_child_objects();

	ModuleData* moduleData;
	if (dynamic_cast<sc_interface*>(top)) moduleData = new ModuleData(top->basename(), true);
	else moduleData = new ModuleData(top->basename());

	ModuleData* childModuleData;
	
	//iterate all children
	for (vector<sc_object*>::size_type i = 0; i < children.size(); i++)
	{
		string kind = children[i]->kind();
		//Then it maps out the modules, threads or methods.
		map<string,string>::iterator type = mTypeMap.find(kind);
		if (type != mTypeMap.end())
		{
			if (type->second == "module")
			{ //if child is a module -> generate moduleData for that module
				childModuleData = createModuleDataTree(children[i]);
				moduleData->addModuleData(childModuleData);
			}
			else if (type->second == "method_process")
			{
				moduleData->addProcess(new ProcessData(children[i]->basename(), "method"));
			}
			else if (type->second == "thread_process")
			{
				moduleData->addProcess(new ProcessData(children[i]->basename(), "thread"));
			}

			//Checks if this child is a Port by using dynamic cast
			sc_port_base* portBase = dynamic_cast<sc_port_base*>(children[i]);
			InOutType inOutType;

			//Checks if the Port is an in_port, out_port or inout_port
			if (type->second == "in_port") inOutType = TYPE_IN;
			else if (type->second == "out_port") inOutType = TYPE_OUT;
			else inOutType = TYPE_INOUT;


			if (portBase)
			{ //If this child is a Port
				mPortInterface.push_back(new ChannelDataInfo(moduleData, (int)(portBase->get_interface()), children[i]->basename(), children[i]->kind(), inOutType));
				moduleData->addPortData(new PortData(children[i]->basename(), children[i]->kind(), type->second));
			}

			//Checks if this child is an interface by using a dynamic cast
			sc_interface* interf = dynamic_cast<sc_interface*>(children[i]);
			if (interf)
			{ //Child is an interface

				if (dynamic_cast<sc_module*>(children[i]))
				{ //This child is also a module
					mInterfaceList.push_back(new ChannelDataInfo(childModuleData, (int)interf, children[i]->basename(), children[i]->kind(), inOutType));			
				}
				else
				{ //Just an interface, not a module
					mInterfaceList.push_back(new ChannelDataInfo(moduleData, (int)interf, children[i]->basename(), children[i]->kind(), inOutType));			
				}
				string portType = type->second;
				if (portType == "module") portType = "inout_port";
				moduleData->addPortData(new PortData(children[i]->basename(), children[i]->kind(), portType));
			}


		}
	}

	return moduleData;
}